local addonName, addon = ...

local grid, settings = CreateFrame('Frame', nil, UIParent)						-- Must use UIParent for uiscale
addon.grid = grid

--[[----------------------------------------------------------------------------
Resolution and windowed mode support
------------------------------------------------------------------------------]]
local HALF_SCREEN_HEIGHT, HALF_SCREEN_WIDTH = 384, 512
local HEIGHT_FACTOR, SQUARE_FACTOR, WIDTH_FACTOR = 1, 1, 1

do
	local nativeInverseRatio, squareFactor = 1, 1

	do	-- Calculate native inverse ratio
		local resolutions = { GetScreenResolutions() }
		local width, height = resolutions[#resolutions]:match("(%d+)x(%d+)")
		nativeInverseRatio = (tonumber(width) or 1) / (tonumber(height) or 1)
	end

	local function OnUpdate(self)
		self:SetScript('OnUpdate', nil)

		local width, height = self:GetSize()
		local resolutionWidth, resolutionHeight = GetCVar('gxResolution'):match("(%d+)x(%d+)")
		resolutionHeight, resolutionWidth = tonumber(resolutionHeight) or 1, tonumber(resolutionWidth) or 1

		HALF_SCREEN_HEIGHT, HALF_SCREEN_WIDTH = height * 0.5, width * 0.5
		HEIGHT_FACTOR, WIDTH_FACTOR = height / resolutionHeight, width / resolutionWidth

		SQUARE_FACTOR = GetCVarBool('gxWindow') and 1 or (resolutionHeight / resolutionWidth) * nativeInverseRatio

		if settings then
			addon.UpdateCenterLines()
			addon.DrawGrid()
		end
	end

	hooksecurefunc('SetCVar', function(cVar, value)
		if cVar ~= 'gxWindow' then return end
		grid:SetScript('OnUpdate', OnUpdate)
	end)

	grid:SetScript('OnSizeChanged', function(self)
		self:SetScript('OnUpdate', OnUpdate)
	end)
end

--[[----------------------------------------------------------------------------
Line support
------------------------------------------------------------------------------]]
local drawnLines, erasedLines = { }, { }

grid:SetFrameStrata('BACKGROUND')
grid:SetFrameLevel(0)
grid:SetAllPoints(nil)
grid:Hide()

local backdrop = WorldFrame:CreateTexture(nil, 'BORDER', nil, -8)				-- Not visible with 'BACKGROUND'
backdrop:SetTexture([[Interface\BUTTONS\WHITE8X8]])
backdrop:SetBlendMode('DISABLE')
backdrop:SetAllPoints(nil)
backdrop:Hide()

local centerLineX = grid:CreateTexture(nil, 'BACKGROUND', nil, -7)
centerLineX:SetPoint('TOP', grid, 'TOP', 0, 0)
centerLineX:SetPoint('BOTTOM', grid, 'BOTTOM', 0, 0)
centerLineX:SetTexture([[Interface\BUTTONS\WHITE8X8]])
centerLineX:SetBlendMode('DISABLE')

local centerLineY = grid:CreateTexture(nil, 'BACKGROUND', nil, -7)
centerLineY:SetPoint('LEFT', grid, 'LEFT', 0, 0)
centerLineY:SetPoint('RIGHT', grid, 'RIGHT', 0, 0)
centerLineY:SetTexture([[Interface\BUTTONS\WHITE8X8]])
centerLineY:SetBlendMode('DISABLE')

local function DrawLine(relPoint1, relPoint2, offsetX, offsetY, SetSize, size)
	local line = erasedLines[#erasedLines]
	if line then
		erasedLines[#erasedLines] = nil
		line:ClearAllPoints()
	else
		if settings.overlay then
			line = grid:CreateTexture(nil, 'OVERLAY', nil, 6)
		else
			line = grid:CreateTexture(nil, 'BACKGROUND', nil, -8)
		end
		line:SetTexture([[Interface\BUTTONS\WHITE8X8]])
		line:SetBlendMode('DISABLE')
		local color = settings.gridColor
		line:SetVertexColor(color[1], color[2], color[3], color[4])
	end
	drawnLines[#drawnLines + 1] = line
	line:SetPoint(relPoint1, grid, relPoint1, offsetX, offsetY)
	line:SetPoint(relPoint2, grid, relPoint2, offsetX, offsetY)
	SetSize(line, size)
	line:Show()
end

local function DrawHorzLines(start, step)
	local size, SetHeight = settings.gridThickness * HEIGHT_FACTOR, backdrop.SetHeight
	for offset = start, HALF_SCREEN_HEIGHT, step do
		DrawLine('LEFT', 'RIGHT', 0, offset, SetHeight, size)
		DrawLine('LEFT', 'RIGHT', 0, -offset, SetHeight, size)
	end
end

local function DrawVertLines(start, step)
	local size, SetWidth = settings.gridThickness * WIDTH_FACTOR, backdrop.SetWidth
	for offset = start, HALF_SCREEN_WIDTH, step do
		DrawLine('TOP', 'BOTTOM', offset, 0, SetWidth, size)
		DrawLine('TOP', 'BOTTOM', -offset, 0, SetWidth, size)
	end
end

local function ForGrid(func, ...)
	for index = 1, #drawnLines do
		func(drawnLines[index], ...)
	end
	for index = 1, #erasedLines do
		func(erasedLines[index], ...)
	end
end

--[[----------------------------------------------------------------------------
Global to addon
------------------------------------------------------------------------------]]
function addon.ApplyColor(field)
	local color = settings[field or "gridColor"]
	if field == "backdropColor" then
		backdrop:SetVertexColor(color[1], color[2], color[3], color[4])
	elseif field == "centerColor" then
		centerLineX:SetVertexColor(color[1], color[2], color[3], color[4])
		centerLineY:SetVertexColor(color[1], color[2], color[3], color[4])
	else
		ForGrid(backdrop.SetVertexColor, color[1], color[2], color[3], color[4])
		if not field then
			addon.ApplyColor("backdropColor")
			addon.ApplyColor("centerColor")
		end
	end
end

function addon.DrawGrid()
	local numErased = #erasedLines
	for index = #drawnLines, 1, -1 do
		numErased = numErased + 1
		erasedLines[numErased] = drawnLines[index]
		drawnLines[index]:Hide()
		drawnLines[index] = nil
	end
	local spacingX, spacingY, squareFactor = settings.spacingX
	if settings.independent then
		spacingY, squareFactor = settings.spacingY, 1
	else
		spacingY, squareFactor = spacingX, settings.squaring and SQUARE_FACTOR or 1
	end
	if settings.absolute then
		DrawVertLines(spacingX, spacingX)
		spacingY = spacingY * squareFactor
		DrawHorzLines(spacingY, spacingY)
	else
		DrawVertLines(spacingX + settings.centerThickness - 1, spacingX + settings.gridThickness - 1)
		DrawHorzLines((spacingY + settings.centerThickness - 1) * squareFactor, (spacingY + settings.gridThickness - 1) * squareFactor)
	end
end

function addon.UpdateCenterLines()
	centerLineX:SetWidth(settings.centerThickness * WIDTH_FACTOR)
	centerLineY:SetHeight(settings.centerThickness * HEIGHT_FACTOR)
end

do
	local overlay, window = false, 1

	function addon.UpdateState()
		backdrop:SetShown((settings.backdropIndependent or not settings.hide) and settings.backdropEnable)
		grid:SetShown(not settings.hide)
		if settings.window ~= window then
			window = settings.window
			local frame = window == 2 and UIParent or window == 3 and WorldFrame or nil
			backdrop:ClearAllPoints()
			backdrop:SetAllPoints(frame)
			grid:ClearAllPoints()
			grid:SetAllPoints(frame)
		end
		if settings.overlay ~= overlay then
			overlay = settings.overlay
			local layer, level, strata, subLayer
			if overlay then
				layer, level, strata, subLayer = 'OVERLAY', 127, 'TOOLTIP', 7
			else
				layer, level, strata, subLayer = 'BACKGROUND', 0, 'BACKGROUND', -7
			end
			grid:SetFrameStrata(strata)
			grid:SetFrameLevel(level)
			ForGrid(backdrop.SetDrawLayer, layer, subLayer - 1)
			centerLineX:SetDrawLayer(layer, subLayer)
			centerLineY:SetDrawLayer(layer, subLayer)
		end
	end
end

--[[----------------------------------------------------------------------------
Initialize
------------------------------------------------------------------------------]]
local function UpdateProfile()
	settings = addon.db.profile
	addon.ApplyColor()
	addon.UpdateState()
	addon.UpdateCenterLines()
	addon.DrawGrid()
end

grid:SetScript('OnEvent', function(self, event, name)
	if addonName ~= name then return end
	self:UnregisterEvent(event)
	self:SetScript('OnEvent', nil)

	addon.db = LibStub('AceDB-3.0'):New(addonName .. "Settings", { profile = addon.defaultSettings }, 'Default')
	UpdateProfile()
	CONFIGMODE_CALLBACKS[addonName] = addon.ConfigMode
	addon.db.RegisterCallback(addon, 'OnProfileChanged', UpdateProfile)
	addon.db.RegisterCallback(addon, 'OnProfileCopied', UpdateProfile)
	addon.db.RegisterCallback(addon, 'OnProfileReset', UpdateProfile)
end)
grid:RegisterEvent('ADDON_LOADED')
